﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace GoodStuff.Silverlight
{
    [TemplatePart(Name = PreviousContentName, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = CurrentContentName, Type = typeof(ContentPresenter))]
    public class TransitionControl : ContentControl
    {
        public const string PreviousContentName = "PreviousContent";
        public const string CurrentContentName = "CurrentContent";

        private ContentPresenter _previousContent;
        private ContentPresenter _currentContent;

        private Storyboard _transition;

        public TransitionControl()
        {
            this.DefaultStyleKey = typeof(TransitionControl);
            EnterDirection = TransitionDirection.Up;
            ExitDirection = TransitionDirection.Left;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _previousContent = this.GetTemplateChild(PreviousContentName) as ContentPresenter;
            _currentContent = this.GetTemplateChild(CurrentContentName) as ContentPresenter;

            if (_currentContent != null)
            {
                _currentContent.Content = this.Content;
            }
        }



        public TransitionDirection EnterDirection
        {
            get { return (TransitionDirection)GetValue(EnterDirectionProperty); }
            set { SetValue(EnterDirectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EnterDirection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EnterDirectionProperty =
            DependencyProperty.Register("EnterDirection", typeof(TransitionDirection), typeof(TransitionControl), null);



        public TransitionDirection ExitDirection
        {
            get { return (TransitionDirection)GetValue(ExitDirectionProperty); }
            set { SetValue(ExitDirectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ExitDirection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ExitDirectionProperty =
            DependencyProperty.Register("ExitDirection", typeof(TransitionDirection), typeof(TransitionControl), null);

        public object PreviousContent
        {
            get { return (DependencyObject)GetValue(PreviousContentProperty); }
            set { SetValue(PreviousContentProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PreviousContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PreviousContentProperty =
            DependencyProperty.Register("PreviousContent", typeof(object), typeof(TransitionControl), new PropertyMetadata(null));

        protected override void OnContentChanged(object oldContent, object newContent)
        {
            //kick in an animation to replace the content.
            base.OnContentChanged(oldContent, newContent);

            StartTransition(oldContent, newContent);
        }

        private void StartTransition(object oldContent, object newContent)
        {
            this.ApplyTemplate();

            _currentContent.Content = newContent;
            _previousContent.Content = oldContent;

            //for now use a hardcoded transition.
            if (_transition != null)
            {
                _transition.Stop();
            }

            TranslateTransform transform = (TranslateTransform)_previousContent.RenderTransform;
            TranslateTransform transformNew = (TranslateTransform)_currentContent.RenderTransform;

            _currentContent.Opacity = 0.0;
            _previousContent.Visibility = Visibility.Visible;

            _transition = new Storyboard();

            double amountIn = (EnterDirection == TransitionDirection.Left || EnterDirection == TransitionDirection.Right)?_currentContent.DesiredSize.Width:_currentContent.DesiredSize.Height;
            double amountOut = (ExitDirection == TransitionDirection.Left || ExitDirection == TransitionDirection.Right)?_currentContent.DesiredSize.Width:_currentContent.DesiredSize.Height;

            TimeSpan duration = TimeSpan.FromSeconds(0.6);

            _transition.Children.Add(CreateDoubleToAnimation(transformNew, amountIn, 0, duration, EasingMode.EaseInOut, EnterDirection));
            _transition.Children.Add(CreateDoubleToAnimation(transform, 0, amountOut, duration, EasingMode.EaseInOut, ExitDirection));

            //opacity animations are fixed.
            _transition.Children.Add(CreateDoubleToAnimation(_currentContent, OpacityProperty, 1.0, duration, EasingMode.EaseOut));
            _transition.Children.Add(CreateDoubleToAnimation(_previousContent, OpacityProperty, 0.0, duration, EasingMode.EaseOut));
            _transition.Begin();
            _transition.Completed += new EventHandler(_transition_Completed);
        }

        private Timeline CreateDoubleToAnimation(TranslateTransform transform, double from, double to, TimeSpan timeSpan, EasingMode easingMode, TransitionDirection direction)
        {
            DependencyProperty property;
            switch (direction)
            {
                case TransitionDirection.Down:
                    transform.Y = from;                    
                    property = TranslateTransform.YProperty;
                    break;
                case TransitionDirection.Up:
                    transform.Y = -from;
                    to = -to; 
                    property = TranslateTransform.YProperty;
                    break;
                case TransitionDirection.Left:
                    transform.X = -from;
                    to = -to;
                    property = TranslateTransform.XProperty;
                    break;
                case TransitionDirection.Right:
                    transform.X = from;
                    property = TranslateTransform.XProperty;
                    break;   
                default:
                    throw new ArgumentOutOfRangeException();
            }

            return CreateDoubleToAnimation(transform, property, to, timeSpan, easingMode);
        }

        void _transition_Completed(object sender, EventArgs e)
        {
            _previousContent.Visibility = Visibility.Collapsed;
        }

        private Timeline CreateDoubleToAnimation(DependencyObject target, DependencyProperty property, double to, TimeSpan duration, EasingMode easingMode)
        {
            DoubleAnimation anim = new DoubleAnimation();
            anim.To = to;
            anim.Duration = new Duration(duration);
            anim.EasingFunction = new QuadraticEase() { EasingMode = easingMode };
            Storyboard.SetTarget(anim, target);
            Storyboard.SetTargetProperty(anim, new PropertyPath(property));
            return anim;
        }  
    }

    public enum TransitionDirection
    {
        Up,
        Down,
        Left,
        Right
    }
}
